#include "AS25DMapRoad.h"
#include "Vector2I.h"
#include "ASComponent.h"

USING_NS_CC;
#define kTextureAtlasInvalidIndex 0xFFFFFFFF
AS25DMapRoadCell::AS25DMapRoadCell()
	:_iTextureAtlasIndex(kTextureAtlasInvalidIndex)
	,_iCellX(0)
	,_iCellY(0)
{
	memset(_vData,0,sizeof(_vData));
}

AS25DMapRoad::AS25DMapRoad():
	_iWidth(0),
	_iHeight(0),
	_iCellW(0),
	_iCellH(0)
{
}

AS25DMapRoad::~AS25DMapRoad()
{
	for (std::map<unsigned int,AS25DMapRoadCell*>::iterator it = _mRoadCells.begin(); it != _mRoadCells.end(); it++)
	{
		if (it->second)
		{
			delete it->second;
		}
	}
	_mRoadCells.clear();
}

AS25DMapRoad* AS25DMapRoad::create(cocos2d::Texture2D *tex,unsigned int width,unsigned int height)
{
	AS25DMapRoad* pRet = new AS25DMapRoad;
	if(pRet&&pRet->init(tex,width,height))
	{
		pRet->autorelease();
	}
	else
	{
		delete pRet;
		pRet = nullptr;
	}
	return pRet;
}
bool AS25DMapRoad::init(cocos2d::Texture2D *tex,unsigned int width,unsigned int height)
{
	if(width>0&&height>0)
	{
		_blendFunc = BlendFunc::ALPHA_PREMULTIPLIED;
		_textureAtlas = new TextureAtlas();
		_iCapacity = width*height;
		_textureAtlas->initWithTexture(tex, _iCapacity);

		updateBlendFunc();
    
		setShaderProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR));

		Size texsize = _textureAtlas->getTexture()->getContentSize();

		_iWidth = width;
		_iHeight = height;
		_iCellW = texsize.width/4;
		_iCellH = texsize.height/4;
		//_textureAtlas->fillWithEmptyQuadsFromIndex(0,_iCapacity);
		
		/*
		V3F_C4B_T2F_Quad quad;
		for (int x = 0; x < _iWidth; x++)
		{
			for (int y = 0; y < _iHeight; y++)
			{
				unsigned index = y+x*_iHeight;
				
				float left        = (0 * _iCellW) / texsize.width;
				float right       = left + _iCellW / texsize.width;
				float top         = (0 * _iCellH) / texsize.height;
				float bottom      = top + _iCellH / texsize.height;

				quad.tl.texCoords.u = left;
				quad.tl.texCoords.v = top;
				quad.tr.texCoords.u = right;
				quad.tr.texCoords.v = top;
				quad.bl.texCoords.u = left;
				quad.bl.texCoords.v = bottom;
				quad.br.texCoords.u = right;
				quad.br.texCoords.v = bottom;

				quad.bl.vertices.x = (float) (x * _iCellW);
				quad.bl.vertices.y = (float) (y * _iCellH);
				quad.bl.vertices.z = 0.0f;
				quad.br.vertices.x = (float)(x * _iCellW + _iCellW);
				quad.br.vertices.y = (float)(y * _iCellH);
				quad.br.vertices.z = 0.0f;
				quad.tl.vertices.x = (float)(x * _iCellW);
				quad.tl.vertices.y = (float)(y * _iCellH + _iCellH);
				quad.tl.vertices.z = 0.0f;
				quad.tr.vertices.x = (float)(x * _iCellW + _iCellW);
				quad.tr.vertices.y = (float)(y * _iCellH + _iCellH);
				quad.tr.vertices.z = 0.0f;

				Color4B color(_displayedColor.r, _displayedColor.g, _displayedColor.b, _displayedOpacity);
				quad.tr.colors = color;
				quad.tl.colors = color;
				quad.br.colors = color;
				quad.bl.colors = color;

				_textureAtlas->insertQuad(&quad,index);
			}
		}
		*/


		return true;
	}
	return false;
}

const AS25DMapRoadCell* AS25DMapRoad::getRoadCell(unsigned int x,unsigned int y) const
{
	unsigned int idx = x*_iHeight+y;
	std::map<unsigned int,AS25DMapRoadCell*>::const_iterator it = _mRoadCells.find(idx);
	if (it != _mRoadCells.end())
	{
		return it->second;
	}
	return nullptr;
}

void AS25DMapRoad::onCellChange(unsigned int x,unsigned int y,bool open)
{
	if (x>=_iWidth||y>=_iHeight)
	{
		return;
	}
	unsigned int idx = x*_iHeight+y;
	std::map<unsigned int,AS25DMapRoadCell*>::iterator it = _mRoadCells.find(idx);
	if (it == _mRoadCells.end())
	{
		if (open)
		{//add
			AS25DMapRoadCell* pCell = new AS25DMapRoadCell;
			pCell->_iCellX = x;
			pCell->_iCellY = y;
			
			unsigned int idxRound;
			std::map<unsigned int,AS25DMapRoadCell*>::iterator itRound;
			//check left cell
			if (x>0)
			{
				idxRound = (x-1)*_iHeight+y;
				//if (idxRound < _iCapacity)
				{
					itRound = _mRoadCells.find(idxRound);
					if (itRound!=_mRoadCells.end())
					{
						if (itRound->second)
						{
							if (itRound->second->_vData[AS25DMapRoadCell::RIGHT] != 1)
							{
								itRound->second->_vData[AS25DMapRoadCell::RIGHT] = 1;
								updateMapAtlas(x-1,y);
							}
							pCell->_vData[AS25DMapRoadCell::LEFT] = 8;
						}
					}
				}
			}
			
			

			//check down cell
			if (y>0)
			{
				idxRound = x*_iHeight+y-1;
				//if (idxRound < _iCapacity)
				{
					itRound = _mRoadCells.find(idxRound);
					if (itRound!=_mRoadCells.end())
					{
						if (itRound->second)
						{
							if (itRound->second->_vData[AS25DMapRoadCell::UP] != 2)
							{
								itRound->second->_vData[AS25DMapRoadCell::UP] = 2;
								updateMapAtlas(x,y-1);
							}
							pCell->_vData[AS25DMapRoadCell::DOWN] = 4;
						}
					}
				}
			}
			
			

			//check right cell
			if (x+1<_iWidth)
			{
				idxRound = (x+1)*_iHeight+y;
				//if (idxRound < _iCapacity)
				{
					itRound = _mRoadCells.find(idxRound);
					if (itRound!=_mRoadCells.end())
					{
						if (itRound->second)
						{
							if (itRound->second->_vData[AS25DMapRoadCell::LEFT] != 8)
							{
								itRound->second->_vData[AS25DMapRoadCell::LEFT] = 8;
								updateMapAtlas(x+1,y);
							}
							pCell->_vData[AS25DMapRoadCell::RIGHT] = 1;
						}
					}
				}
			}
			

			//check up cell
			if (y+1<_iHeight)
			{
				idxRound = x*_iHeight+y+1;
				//if (idxRound < _iCapacity)
				{
					itRound = _mRoadCells.find(idxRound);
					if (itRound!=_mRoadCells.end())
					{
						if (itRound->second)
						{
							if (itRound->second->_vData[AS25DMapRoadCell::DOWN] != 4)
							{
								itRound->second->_vData[AS25DMapRoadCell::DOWN] = 4;
								updateMapAtlas(x,y+1);
							}
							pCell->_vData[AS25DMapRoadCell::UP] = 2;
						}
					}
				}
			}
			
			
			//update self 
			_mRoadCells.insert(std::make_pair(idx,pCell));
			updateMapAtlas(x,y);
		}
	}
	else
	{
		if (!open)
		{//remove
			unsigned int idxRound;
			std::map<unsigned int,AS25DMapRoadCell*>::iterator itRound;
			//check left cell
			if (x>0)
			{
				idxRound = (x-1)*_iHeight+y;
				//if (idxRound < _iCapacity)
				{
					itRound = _mRoadCells.find(idxRound);
					if (itRound!=_mRoadCells.end())
					{
						if (itRound->second)
						{
							if (itRound->second->_vData[AS25DMapRoadCell::RIGHT] != 0)
							{
								itRound->second->_vData[AS25DMapRoadCell::RIGHT] = 0;
								updateMapAtlas(x-1,y);
							}
						}
					}
				}
			}
			
			

			//check down cell
			if (y>0)
			{
				idxRound = x*_iHeight+y-1;
				//if (idxRound < _iCapacity)
				{
					itRound = _mRoadCells.find(idxRound);
					if (itRound!=_mRoadCells.end())
					{
						if (itRound->second)
						{
							if (itRound->second->_vData[AS25DMapRoadCell::UP] != 0)
							{
								itRound->second->_vData[AS25DMapRoadCell::UP] = 0;
								updateMapAtlas(x,y-1);
							}
						}
					}
				}
			}
			
			

			//check right cell
			if (x+1<_iWidth)
			{
				idxRound = (x+1)*_iHeight+y;
				//if (idxRound < _iCapacity)
				{
					itRound = _mRoadCells.find(idxRound);
					if (itRound!=_mRoadCells.end())
					{
						if (itRound->second)
						{
							if (itRound->second->_vData[AS25DMapRoadCell::LEFT] != 0)
							{
								itRound->second->_vData[AS25DMapRoadCell::LEFT] = 0;
								updateMapAtlas(x+1,y);
							}
						}
					}
				}
			}
			

			//check up cell
			if (y+1<_iHeight)
			{
				idxRound = x*_iHeight+y+1;
				//if (idxRound < _iCapacity)
				{
					itRound = _mRoadCells.find(idxRound);
					if (itRound!=_mRoadCells.end())
					{
						if (itRound->second)
						{
							if (itRound->second->_vData[AS25DMapRoadCell::DOWN] != 0)
							{
								itRound->second->_vData[AS25DMapRoadCell::DOWN] = 0;
								updateMapAtlas(x,y+1);
							}
						}
					}
				}
			}
			
			
			//update self 
			updateMapAtlas(x,y,false);
			delete it->second;
			_mRoadCells.erase(it);
		}
	}
}

void AS25DMapRoad::updateMapAtlas(unsigned int x,unsigned int y,bool open)
{
	if (x<_iWidth&&y<_iHeight)
	{
		unsigned int index = x*_iHeight+y;
		std::map<unsigned int,AS25DMapRoadCell*>::iterator it = _mRoadCells.find(index);
		if (it != _mRoadCells.end())
		{
			if (open)
			{
				int cx = (_iCellW>>1);
				int cy = (_iCellH>>1);
				Vector2I vx(cx,-cy);
				Vector2I vy(cx,cy);
			
				int value = it->second->_vData[0]+it->second->_vData[1]+it->second->_vData[2]+it->second->_vData[3];
				int tx = value/4;
				int ty = value%4;
				float left        = tx*0.25;
				float right       = (tx+1)*0.25;
				float top         = ty*0.25;
				float bottom      = (ty+1)*0.25;
				
				V3F_C4B_T2F_Quad quad;// = &((_textureAtlas->getQuads())[index]);
				quad.tl.texCoords.u = left;
				quad.tl.texCoords.v = top;
				quad.tr.texCoords.u = right;
				quad.tr.texCoords.v = top;
				quad.bl.texCoords.u = left;
				quad.bl.texCoords.v = bottom;
				quad.br.texCoords.u = right;
				quad.br.texCoords.v = bottom;
				Vector2I vpos = vx * x + vy * y;
				quad.bl.vertices.x = (float) (vpos.x);
				quad.bl.vertices.y = (float) (vpos.y - cy);
				quad.bl.vertices.z = 0.0f;
				quad.br.vertices.x = (float)(vpos.x + (cx<<1));
				quad.br.vertices.y = (float)(vpos.y - cy);
				quad.br.vertices.z = 0.0f;
				quad.tl.vertices.x = (float)(vpos.x);
				quad.tl.vertices.y = (float)(vpos.y + cy);
				quad.tl.vertices.z = 0.0f;
				quad.tr.vertices.x = (float)(vpos.x + (cx<<1));
				quad.tr.vertices.y = (float)(vpos.y + cy);
				quad.tr.vertices.z = 0.0f;

				Color4B color(_displayedColor.r, _displayedColor.g, _displayedColor.b, _displayedOpacity);
				quad.tr.colors = color;
				quad.tl.colors = color;
				quad.br.colors = color;
				quad.bl.colors = color;
				if (it->second->_iTextureAtlasIndex==kTextureAtlasInvalidIndex)
				{
					it->second->_iTextureAtlasIndex = _textureAtlas->getTotalQuads();
					_textureAtlas->insertQuad(&quad,it->second->_iTextureAtlasIndex);
				}
				else
				{
					_textureAtlas->updateQuad(&quad,it->second->_iTextureAtlasIndex);
				}
			}
			else
			{
				if (it->second->_iTextureAtlasIndex!=kTextureAtlasInvalidIndex)
				{
					_textureAtlas->removeQuadAtIndex(it->second->_iTextureAtlasIndex);
					//
					for (std::map<unsigned int,AS25DMapRoadCell*>::iterator ittemp = _mRoadCells.begin(); ittemp != _mRoadCells.end(); ittemp++)
					{
						if (ittemp->second&&ittemp->second->_iTextureAtlasIndex>it->second->_iTextureAtlasIndex)
						{
							ittemp->second->_iTextureAtlasIndex--;
						}
					}
				}
				else
				{
					ASAssert(0,"AS25DMapRoad::updateMapAtlas invalid _iTextureAtlasIndex");
				}
			}
		}
	}
}

void AS25DMapRoad::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
	//_renderCommand.init(_globalZOrder);
 //   _renderCommand.func = CC_CALLBACK_0(AS25DMapRoad::onDraw, this, transform, transformUpdated);
 //   renderer->addCommand(&_renderCommand);
	_batchCommand.init(
                       _globalZOrder,
					   getGLProgram(),
                       _blendFunc,
                       _textureAtlas,
                       transform);
    renderer->addCommand(&_batchCommand);
}
//void AS25DMapRoad::onDraw(const kmMat4 &transform, bool transformUpdated)
//{
//
//}

Texture2D* AS25DMapRoad::getTexture(void) const
{
    return _textureAtlas->getTexture();
}

void AS25DMapRoad::setTexture(Texture2D *texture)
{
    _textureAtlas->setTexture(texture);
	Size size = _textureAtlas->getTexture()->getContentSize();
	if(_iCellW!=size.width/4||_iCellW!=size.height/4)
	{
		_iCellW = size.width/4;
		_iCellH = size.height/4;
    }

	updateBlendFunc();
}


void AS25DMapRoad::updateBlendFunc(void)
{
    if (! _textureAtlas->getTexture()->hasPremultipliedAlpha())
        _blendFunc = BlendFunc::ALPHA_NON_PREMULTIPLIED;
}

// CocosNodeTexture protocol
void AS25DMapRoad::setBlendFunc(const BlendFunc &blendFunc)
{
    _blendFunc = blendFunc;
}

const BlendFunc& AS25DMapRoad::getBlendFunc(void) const
{
    return _blendFunc;
}